package com.example.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.example.commons.SysConstant;
import com.example.config.AlipayConfig;
import com.example.entity.Order;
import com.example.service.AlipayService;
import com.example.service.OrderService;
import com.example.vo.BaseResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @author 游家纨绔
 * @dateTime 2023-09-11 07:30
 * @apiNote TODO 支付宝接口
 */
@Slf4j
@Controller
public class AlipayController {

	@Autowired
	private AlipayService alipayService;
	@Autowired
	private OrderService orderService;
	@Autowired
	private AlipayConfig alipayConfig;

	// 支付接口
	@GetMapping("/trade")
	public void createTrade(HttpServletResponse response) throws IOException {
		// 创建支付
		String trade = alipayService.createTrade();
		response.setContentType("text/html;charset=utf-8");
		response.getWriter().println(trade);
	}

	// 异步通知
	@ResponseBody
	@PostMapping("/trade/notify")
	public void tradeNotify(@RequestParam Map<String, String> params) {
		System.out.println("异步请求中的所有参数: " + params);
		System.out.println("异步请求中获取--商户订单号: " + params.get("out_trade_no") + ", 支付交易号: " + params.get("trade_no") + ", 支付成功");

		// 1、验证支付宝公钥，字符编码，签名方式是否一致
		boolean singnature = false;
		try {
			// rsaCheckV1()：仅验证明确提供的参数
			// rsaCheckV2()：会自动过滤掉签名参数(sign)和签名类型参数(sign_type)【推荐】
			singnature = AlipaySignature.rsaCheckV2(params, alipayConfig.getAlipayPublicKey(),
			                                        alipayConfig.getCharset(), alipayConfig.getSignType()
			);
		} catch (AlipayApiException e) {
			throw new RuntimeException(e);
		}
		if (!singnature) {
			log.info("验证失败，验证签名失败!");
		}

		// 2、验证商户发送的订单号是否与返回的一致
		String out_trade_no = params.get("out_trade_no");
		Order order = orderService.getByTradeNo(out_trade_no);
		if (order == null) {
			log.info("验证失败，订单不存在!");
		}

		// 3、验证金额已发送的是否一致(实际工作开发涉及金额的话使用 BigDecimal，这里为了方便才使用的 double)
		String total_amount = params.get("total_amount");
		if (order.getTotalAmount() != Double.parseDouble(total_amount)) {
			log.info("验证失败，金额不一致!");
		}

		// 4、验证支付宝返回的状态是否一致
		String trade_status = params.get("trade_status");
		if (!SysConstant.TRADE_SUCCESS.equals(trade_status)) {
			log.info("验证失败，交易状态不一致!");
		}

		// 5、修改订单的状态
		orderService.updateOrderStatus(order, SysConstant.TRADE_SUCCESS);
	}

	// 同步通知
	@SneakyThrows
	@GetMapping("/trade/success")
	public void tradeSuccess(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html;charset=utf-8");
		response.getWriter().println("恭喜您～ 商户订单号: " + request.getParameter("out_trade_no") + ", 支付交易号: " + request.getParameter("trade_no") + ", 支付成功!");
	}

	// 关闭订单；例如在规定时间内未完成支付
	@ResponseBody
	@PostMapping("/trade/closeTrade")
	public BaseResult closeTrade(@RequestParam String orderNo) {
		try {
			alipayService.closeTrade(orderNo);
		} catch (AlipayApiException e) {
			return BaseResult.fail("取消订单失败!");
		}
		return BaseResult.success("取消订单成功!");
	}

	// 退款：已经支付成功后，因商家或者客户原因导致交易不能完成
	@ResponseBody
	@PostMapping("/trade/refund")
	public BaseResult refund(@RequestParam String orderNo) {
		try {
			alipayService.refund(orderNo);
		} catch (AlipayApiException e) {
			return BaseResult.fail("退款失败!");
		}
		return BaseResult.success("退款成功!");
	}

	// 查询退款信息
	@ResponseBody
	@GetMapping("/trade/queryRefund")
	public BaseResult queryRefund(@RequestParam String orderNo) {
		try {
			String queryRefund = alipayService.queryRefund(orderNo);
			return BaseResult.success(queryRefund);
		} catch (AlipayApiException e) {
			throw new RuntimeException(e);
		}
	}

}
